/*
	rand.cc		Random board number generator
	Copyright (c) 2004 Kriang Lerdsuwanakij
	email:		lerdsuwa@users.sourceforge.net

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "config.h"

#include "rand.h"
#include "randboard.h"
#include "order.h"
#include "gtstream.h"
#include <stdexcept>

#ifdef _
#undef _
#endif

#ifdef N_
#undef N_
#endif

#include <libintl.h>
#define _(x) gettext(x)
#define N_(x) x

// Pieces are randomly placed according to the following steps:
// All '0' position must be placed before a piece is added on
// any '1' position, and so on.
// . . . . . . . .
// . . 3 2 2 3 . .
// . 3 2 1 1 2 3 .
// . 2 1 0 0 1 2 .
// . 2 1 0 0 1 2 .
// . 3 2 1 1 2 3 .
// . . 3 2 2 2 . .
// . . . . . . . .

static const int NUM_RANDOM_STEP0 = 4;
static const int NUM_RANDOM_STEP1 = 8;
static const int NUM_RANDOM_STEP2 = 12;
static const int NUM_RANDOM_STEP3 = 8;

static const int TOTAL_NUM_RANDOM_STEP = NUM_RANDOM_STEP0
					 + NUM_RANDOM_STEP1
					 + NUM_RANDOM_STEP2
					 + NUM_RANDOM_STEP3;
static const int MAX_NUM_RANDOM_STEP = 12;

static int random_step0[NUM_RANDOM_STEP0] = { D4, E4,
					      D5, E5 };

static int random_step1[NUM_RANDOM_STEP1] = {	  D3, E3,
					      C4,	  F4, 
					      C5,	  F5,
					          D6, E6 };

static int random_step2[NUM_RANDOM_STEP2] = {	      D2, E2,
						  C3,	      F3,
					      B4,		  G4,
					      B5,		  G5,
						  C6,	      F6,
						      D7, E7 };

static int random_step3[NUM_RANDOM_STEP3] = {	  C2, F2,
					      B3,	  G3, 
					      B6,	  G6,
					          C7, F7 };

// Valid values for num are 4, 6, 8, ..., 32
// Reentrant: only write to local variable
void	random_board(byte_board_info *b, int num)
{
	if (num < NUM_RANDOM_STEP0 || num > TOTAL_NUM_RANDOM_STEP
	    || (num & 1)) {
		gtstream bufstr;
		gtout(bufstr, _("invalid number of pieces %$ in random_board"))
			<< num;
		throw std::range_error(bufstr.str());
	}

	byte_board_type init;
	for (int i = 0; i < 64; ++i)
		init[i] = EMPTY;

	int pos_array_step[MAX_NUM_RANDOM_STEP];
	int pos_array[TOTAL_NUM_RANDOM_STEP];
	int cur_pos_array = 0;

					// Do step 0
	int cur_step = NUM_RANDOM_STEP0;
	int cur_step_left = NUM_RANDOM_STEP0;
	int num_left = num - cur_step;

	for (int i = 0; i < NUM_RANDOM_STEP0; ++i)
		pos_array_step[i] = random_step0[i];

	while (cur_step) {
		int pos = (cur_step_left > 1) ? get_random(cur_step_left) : 0;
		pos_array[cur_pos_array++] = pos_array_step[pos];
		--cur_step;
		--cur_step_left;
		pos_array_step[pos] = pos_array_step[cur_step_left];
	}

					// Do step 1
	if (num_left) {
		if (num_left < NUM_RANDOM_STEP1)
			cur_step = num_left;
		else
			cur_step = NUM_RANDOM_STEP1;
		cur_step_left = NUM_RANDOM_STEP1;
		num_left -= cur_step;

		for (int i = 0; i < NUM_RANDOM_STEP1; ++i)
			pos_array_step[i] = random_step1[i];

		while (cur_step) {
			int pos = (cur_step_left > 1) ? get_random(cur_step_left) : 0;
			pos_array[cur_pos_array++] = pos_array_step[pos];
			--cur_step;
			--cur_step_left;
			pos_array_step[pos] = pos_array_step[cur_step_left];
		}
	}

					// Do step 2
	if (num_left) {
		if (num_left < NUM_RANDOM_STEP2)
			cur_step = num_left;
		else
			cur_step = NUM_RANDOM_STEP2;
		cur_step_left = NUM_RANDOM_STEP2;
		num_left -= cur_step;

		for (int i = 0; i < NUM_RANDOM_STEP2; ++i)
			pos_array_step[i] = random_step2[i];

		while (cur_step) {
			int pos = (cur_step_left > 1) ? get_random(cur_step_left) : 0;
			pos_array[cur_pos_array++] = pos_array_step[pos];
			--cur_step;
			--cur_step_left;
			pos_array_step[pos] = pos_array_step[cur_step_left];
		}
	}

					// Do step 3
	if (num_left) {
		if (num_left < NUM_RANDOM_STEP3)
			cur_step = num_left;
		else
			cur_step = NUM_RANDOM_STEP3;
		cur_step_left = NUM_RANDOM_STEP3;
		num_left -= cur_step;

		for (int i = 0; i < NUM_RANDOM_STEP3; ++i)
			pos_array_step[i] = random_step3[i];

		while (cur_step) {
			int pos = (cur_step_left > 1) ? get_random(cur_step_left) : 0;
			pos_array[cur_pos_array++] = pos_array_step[pos];
			--cur_step;
			--cur_step_left;
			pos_array_step[pos] = pos_array_step[cur_step_left];
		}
	}

	cur_step = cur_pos_array;
	int color = BLACK;
	while (cur_step) {
		int pos = (cur_step > 1) ? get_random(cur_step) : 0;
		init[pos_array[pos]] = color;
		color = switch_color(color);
		--cur_step;
		pos_array[pos] = pos_array[cur_step];
	}
	*b = &init;
}

